#pragma once

#include "Assert.h"
#include "CoreApi.h"
using namespace loco;

void test_Matrix4()
{
	// constructor
	{
		Matrix4 m1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		ASSERT(m1.m[0][0] == 1);
		ASSERT(m1.m[0][1] == 2);
		ASSERT(m1.m[0][2] == 3);
		ASSERT(m1.m[0][3] == 4);
		ASSERT(m1.m[1][0] == 5);
		ASSERT(m1.m[1][1] == 6);
		ASSERT(m1.m[1][2] == 7);
		ASSERT(m1.m[1][3] == 8);
		ASSERT(m1.m[2][0] == 9);
		ASSERT(m1.m[2][1] == 10);
		ASSERT(m1.m[2][2] == 11);
		ASSERT(m1.m[2][3] == 12);
		ASSERT(m1.m[3][0] == 13);
		ASSERT(m1.m[3][1] == 14);
		ASSERT(m1.m[3][2] == 15);
		ASSERT(m1.m[3][3] == 16);

		Vector4 c0(1, 2, 3, 4);
		Vector4 c1(5, 6, 7, 8);
		Vector4 c2(9, 10, 11, 12);
		Vector4 c3(13, 14, 15, 16);
		Matrix4 m2(c0, c1, c2, c3);
		ASSERT(m2.m[0][0] == 1);
		ASSERT(m2.m[1][0] == 2);
		ASSERT(m2.m[2][0] == 3);
		ASSERT(m2.m[3][0] == 4);
		ASSERT(m2.m[0][1] == 5);
		ASSERT(m2.m[1][1] == 6);
		ASSERT(m2.m[2][1] == 7);
		ASSERT(m2.m[3][1] == 8);
		ASSERT(m2.m[0][2] == 9);
		ASSERT(m2.m[1][2] == 10);
		ASSERT(m2.m[2][2] == 11);
		ASSERT(m2.m[3][2] == 12);
		ASSERT(m2.m[0][3] == 13);
		ASSERT(m2.m[1][3] == 14);
		ASSERT(m2.m[2][3] == 15);
		ASSERT(m2.m[3][3] == 16);
	}

	// near_equal
	{
		Matrix4 m = Matrix4::identity;
		ASSERT(Matrix4::near_equal(Matrix4::identity, m));

		m.m[1][2] += 0.5 * DELTA;
		ASSERT(Matrix4::near_equal(Matrix4::identity, m));

		m.m[1][2] += DELTA;
		ASSERT(!Matrix4::near_equal(Matrix4::identity, m));
	}

	// operator+
	{
		Matrix4 m1 = Matrix4::identity;
		Matrix4 m2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		ASSERT((m1 + m2) == Matrix4(2, 2, 3, 4, 5, 7, 7, 8, 9, 10, 12, 12, 13, 14, 15, 17));
	}

	// operator-
	{
		Matrix4 m1 = Matrix4::identity;
		Matrix4 m2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		ASSERT((m2 - m1) == Matrix4(0, 2, 3, 4, 5, 5, 7, 8, 9, 10, 10, 12, 13, 14, 15, 15));
	}

	// operator*
	{
		Matrix4 m1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		Matrix4 m2(10, 2, 3, 4, 5, 0, 2, 8, 9, 10, 11, 2, 13, 14, 8, 0);
		Matrix4 result = m1 * m2;
		for (uint32 y = 0; y < 4; y++)
		{
			
			for (uint32 x = 0; x < 4; x++)
			{
				float tmp = 0.0f;
				for (uint32 i = 0; i < 4; i++)
				{
					tmp += m1.m[y][i] * m2.m[i][x];
				}
				ASSERT(result.m[y][x] == tmp);
			}
		}
	}

	// operator*=
	{
		Matrix4 m1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		Matrix4 m2(10, 2, 3, 4, 5, 0, 2, 8, 9, 10, 11, 2, 13, 14, 8, 0);
		Matrix4 result = m1;
		result *= m2;
		for (uint32 y = 0; y < 4; y++)
		{

			for (uint32 x = 0; x < 4; x++)
			{
				float tmp = 0.0f;
				for (uint32 i = 0; i < 4; i++)
				{
					tmp += m1.m[y][i] * m2.m[i][x];
				}
				ASSERT(result.m[y][x] == tmp);
			}
		}
	}

	// operator*
	{
		Matrix4 m1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		Vector4 v1(1.0f, 2.0f, 3.0f, 4.0f);
		Vector4 result = m1 * v1;

		float tmp;

		tmp =  m1.m[0][0] * v1.x + m1.m[0][1] * v1.y + m1.m[0][2] * v1.z + m1.m[0][3] * v1.w;
		ASSERT(tmp == result.x);
		tmp = m1.m[1][0] * v1.x + m1.m[1][1] * v1.y + m1.m[1][2] * v1.z + m1.m[1][3] * v1.w;
		ASSERT(tmp == result.y);
		tmp = m1.m[2][0] * v1.x + m1.m[2][1] * v1.y + m1.m[2][2] * v1.z + m1.m[2][3] * v1.w;
		ASSERT(tmp == result.z);
		tmp = m1.m[3][0] * v1.x + m1.m[3][1] * v1.y + m1.m[3][2] * v1.z + m1.m[3][3] * v1.w;
		ASSERT(tmp == result.w);
	}

	// operator==
	{
		Matrix4 m1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		Matrix4 m2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		Matrix4 m3(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 16);
		ASSERT(m1 == m2);
		ASSERT(!(m1 == m3));
	}

	// operator!=
	{
		Matrix4 m1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		Matrix4 m2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
		Matrix4 m3(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 16);
		ASSERT(!(m1 != m2));
		ASSERT(m1 != m3);
	}

	// transpose
	{
		Matrix4 m1(3, 2, 0, 1, 4, 0, 1, 2, 3, 0, 2, 1, 9, 2, 3, 1);
		Matrix4 m2 = m1.transpose();
		for (uint32 y = 0; y < 4; y++)
		{

			for (uint32 x = 0; x < 4; x++)
			{
				ASSERT(m1.m[y][x] == m2.m[x][y]);
			}
		}
	}

	// determinant
	{
		Matrix4 m1(3, 2, 0, 1, 4, 0, 1, 2, 3, 0, 2, 1, 9, 2, 3, 1);
		float det = m1.determinant();
		ASSERT(det == 24);
	}

	// inverse
	{
		Matrix4 m1(3, 2, 0, 1, 4, 0, 1, 2, 3, 0, 2, 1, 9, 2, 3, 1);
		Matrix4 m2 = m1.inverse();
		bool b = Matrix4::near_equal(m1 * m2, Matrix4::identity);
	}
}